package com.qd.system.service.role;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.cdqidi.dto.ApiResult;
import com.cdqidi.dto.ApiResultWrapper;
import com.cdqidi.exception.ApiException;
import com.qd.common.sys.constant.Constants;
import com.qd.common.sys.domain.entity.meun.MenuDTO;
import com.qd.common.sys.domain.entity.person.PersonRoleAllDTO;
import com.qd.common.sys.domain.entity.role.RoleDTO;
import com.qd.common.sys.domain.entity.role.RoleMenuRelationsDTO;
import com.qd.common.sys.domain.entity.role.RolePersonRelationsDTO;
import com.qd.common.sys.enums.BaseTableEnum;
import com.qd.system.model.PersonRole;
import com.qd.system.model.Role;
import com.qd.system.model.RoleMenu;
import com.qd.system.service.dict.MbUtil;
import com.qd.system.service.menu.MenuService;
import com.qd.system.service.person.PersonRoleService;
import com.qd.system.service.user.UserOperatorService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;

/**
 * 角色辅助类
 *
 * @author sjk
 */
@Component
@RequiredArgsConstructor
public class RoleHoldService {

    private final RoleService roleService;
    private final RoleMenuService roleMenuService;
    private final PersonRoleService personRoleService;
    private final MenuService menuService;

    @Transactional(rollbackFor = Exception.class)
    public RoleDTO saveRole(RoleDTO RoleDTO) {
        checkMb(RoleDTO);
        final String roleId = IdWorker.getIdStr();
        RoleDTO.setRoleId(roleId);
        RoleDTO.setAddTime(LocalDateTime.now());
        final List<String> menuList = RoleDTO.getMenuList();
        if (null == menuList || menuList.isEmpty()) {
            roleMenuService.clearCacheByRoleId(roleId);
        } else {
            final List<RoleMenu> roleMenuList = new ArrayList<>(menuList.size());
            RoleMenu roleMenu;
            for (String s : menuList) {
                roleMenu = new RoleMenu();
                roleMenu.setAddTime(RoleDTO.getAddTime());
                roleMenu.setMenuId(s);
                roleMenu.setRoleId(roleId);
                roleMenuList.add(roleMenu);
            }
            roleMenuService.saveBatch(roleMenuList);
            roleMenuList.clear();
            menuList.clear();
        }
        return roleService.save(RoleDTO);
    }

    @Transactional(rollbackFor = Exception.class)
    public ApiResultWrapper<Object> updateRole(RoleDTO RoleDTO) {
        final String roleId = RoleDTO.getRoleId();
        checkMb(RoleDTO);
        checkExists(RoleDTO.getRoleId());
        final LocalDateTime addTime = LocalDateTime.now();
        final List<String> menuList = RoleDTO.getMenuList();
        if (null == menuList || menuList.isEmpty()) {
            roleMenuService.clearCacheByRoleId(roleId);
        } else {
            final List<RoleMenu> roleMenuList = new ArrayList<>(menuList.size());
            RoleMenu roleMenu;
            for (String s : menuList) {
                roleMenu = roleMenuService.getRoleMenuByRoleIdAndMenuId(RoleDTO.getRoleId(), s);
                if (null == roleMenu) {
                    roleMenu = new RoleMenu();
                    roleMenu.setAddTime(addTime);
                    roleMenu.setMenuId(s);
                    roleMenu.setRoleId(RoleDTO.getRoleId());
                    roleMenuList.add(roleMenu);
                }
            }
            if (!roleMenuList.isEmpty()) {
                roleMenuService.saveBatch(roleMenuList);
            }
            roleMenuList.clear();
            menuList.clear();
        }
        roleService.update(RoleDTO);
        clearPersonRoleCache(roleId);
        return ApiResult.success();
    }

    @Transactional(rollbackFor = Exception.class)
    public void removeById(String roleId) {
        checkExists(roleId);
        isDelRelation(roleId);
        roleService.removeById(roleId);
    }

    private void isDelRelation(String roleId) {
        final List<PersonRole> personRoleList = personRoleService.getPersonRoleByRoleId(roleId);
        if (!personRoleList.isEmpty()) {
            throw new ApiException("请先删除角色和人员的关系信息");
        }
        final List<RoleMenu> roleMenuList = roleMenuService.getMenuByRoleId(roleId);
        if (!roleMenuList.isEmpty()) {
            throw new ApiException("请先删除角色和菜单的关系信息");
        }

    }

    @Transactional(rollbackFor = Exception.class)
    public void removeByIds(List<String> roleIds) {
        List<String> existsRoleList = roleService.getBaseMapper().getIds(roleIds);
        if (existsRoleList.isEmpty()) {
            throw new ApiException("角色ID不存在" + Arrays.toString(roleIds.toArray()));
        }
        if (roleIds.size() != existsRoleList.size()) {
            throw new ApiException("菜单信息不存在" + Arrays.toString(roleIds.stream().filter(roleId -> !existsRoleList.contains(roleId)).toArray()));
        }
        roleIds.forEach(this::isDelRelation);
        roleService.removeBatchIds(roleIds);
    }


    private void checkExists(String roleId) {
        final Role role = roleService.getById(roleId);
        if (null == role) {
            throw new ApiException("角色信息不存在，不能修改");
        }
    }

    private void checkMb(RoleDTO RoleDTO) {
        if (checkRoleCodeUnique(RoleDTO)) {
            throw new ApiException("角色编码已经存在");
        }
        if (checkRoleNameUnique(RoleDTO)) {
            throw new ApiException("角色名称已经存在");
        }
        if (MbUtil.isNotExists(RoleDTO.getDataScope(), BaseTableEnum.DATA_SCOPE)) {
            throw new ApiException("数据权限值错误");
        }
        if (!MbUtil.isExists(RoleDTO.getRoleStatus())) {
            throw new ApiException("角色状态值错误");
        }
    }

    /**
     * 判断角色名字是否唯一
     *
     * @param RoleDTO 角色对象
     * @return true or false
     */
    private boolean checkRoleNameUnique(RoleDTO RoleDTO) {
        boolean isExists;
        final QueryWrapper<Role> queryWrapper = new QueryWrapper<>();
        if (StringUtils.hasLength(RoleDTO.getRoleParentId())) {
            queryWrapper.eq("role_parent_id", RoleDTO.getRoleParentId());
        }
        queryWrapper.eq("role_name", RoleDTO.getRoleName());
        final Role role = roleService.getOne(queryWrapper);
        if (null == role) {
            isExists = false;
        } else {
            if (StringUtils.hasLength(role.getRoleId())) {
                isExists = !role.getRoleId().equals(RoleDTO.getRoleId());
            } else {
                isExists = true;
            }
            role.freeData();
        }
        return isExists;
    }

    /**
     * 判断角色ID是否唯一
     *
     * @param RoleDTO 角色对象
     * @return true or false
     */
    private boolean checkRoleCodeUnique(RoleDTO RoleDTO) {
        boolean isExists;
        final Role role = roleService.getUniqueByRoleCode(RoleDTO.getRoleCode());
        if (null == role) {
            isExists = false;
        } else {
            if (StringUtils.hasLength(RoleDTO.getRoleId())) {
                isExists = !role.getRoleId().equals(RoleDTO.getRoleId());
            } else {
                isExists = true;
            }
            role.freeData();
        }
        return isExists;
    }

    /**
     * 清理用户和角色的缓存信息
     *
     * @param roleId 角色ID
     */
    private void clearPersonRoleCache(String roleId) {
        final List<PersonRole> roles = personRoleService.getPersonRoleByRoleId(roleId);
        if (!roles.isEmpty()) {
            for (PersonRole role : roles) {
                personRoleService.clearCache(role);
            }
        }
    }

    @Transactional(readOnly = true)
    public RoleDTO getByIdDto(String roleId) {
        final RoleDTO RoleDTO = roleService.getByIdDto(roleId);
        if (null != RoleDTO) {
            final List<String> menuList = roleMenuService.getMenuStrByRoleId(RoleDTO.getRoleId());
            if (null != menuList && !menuList.isEmpty()) {
                RoleDTO.setMenuList(menuList);
            }
        }
        return RoleDTO;
    }

    @Transactional(rollbackFor = Exception.class)
    public ApiResultWrapper<Object> setRoleMenuRelations(RoleMenuRelationsDTO RoleMenuRelationsDTO) {
        checkRoleId(RoleMenuRelationsDTO.getRoleId());
        final List<String> menuIds = RoleMenuRelationsDTO.getMenuList();
        final List<MenuDTO> menuTreeAll = menuService.getMenuTreeAll();
        final List<String> addMenuIds = new ArrayList<>();
        for (String menuId : menuIds) {
            buildParentId(menuId, menuTreeAll, addMenuIds, RoleMenuRelationsDTO);
        }
        if (!addMenuIds.isEmpty()) {
            RoleMenuRelationsDTO.getMenuList().addAll(addMenuIds);
        }
        roleMenuService.setRoleMenuRelations(RoleMenuRelationsDTO);
        return ApiResult.success("操作成功");
    }

    private void buildParentId(String menuId, List<MenuDTO> menuTreeAll, List<String> addMenuIds, RoleMenuRelationsDTO relationsDto) {
        final List<String> menuList = relationsDto.getMenuList();
        if (null == menuList || menuList.isEmpty()) {
            return;
        }
        final Optional<MenuDTO> optional = menuTreeAll.stream().filter(menu -> StringUtils.hasLength(menu.getMenuId()) && menu.getMenuId().equals(menuId)).findFirst();
        if (optional.isPresent()) {
            final String parentId = optional.get().getParentId();
            if (StringUtils.hasLength(parentId)) {
                if (parentId.equals(Constants.DEFAULT_TREE_PARENT_ID)) {
                    return;
                }
                if (addMenuIds.contains(parentId)) {
                    return;
                }
                if (!menuList.contains(parentId)) {
                    addMenuIds.add(parentId);
                }
                buildParentId(parentId, menuTreeAll, addMenuIds, relationsDto);
            }
        }
    }

    @Transactional(rollbackFor = Exception.class)
    public ApiResultWrapper<Object> setRolePersonRelations(RolePersonRelationsDTO RolePersonRelationsDTO) {
        checkRoleId(RolePersonRelationsDTO.getRoleId());
        personRoleService.setRolePersonRelations(RolePersonRelationsDTO);
        return ApiResult.success("操作成功");
    }

    @Transactional(rollbackFor = Exception.class)
    public ApiResultWrapper<Object> deleteRoleMenuRelations(RoleMenuRelationsDTO RoleMenuRelationsDTO) {
        checkRoleId(RoleMenuRelationsDTO.getRoleId());
        roleMenuService.deleteRoleMenuRelations(RoleMenuRelationsDTO);
        return ApiResult.success("删除成功");
    }

    @Transactional(rollbackFor = Exception.class)
    public ApiResultWrapper<Object> deleteRolePersonRelations(RolePersonRelationsDTO RolePersonRelationsDTO) {
        checkRoleId(RolePersonRelationsDTO.getRoleId());
        personRoleService.deleteRolePersonRelations(RolePersonRelationsDTO);
        return ApiResult.success("删除成功");
    }

    public void checkRoleId(String roleId) {
        if (Boolean.FALSE.equals(roleService.isExistsById(roleId))) {
            throw new ApiException("角色ID不存在");
        }
    }

    @Transactional(readOnly = true)
    public IPage<PersonRoleAllDTO> rolePersonPage(IPage<PersonRoleAllDTO> iPage, PersonRoleAllDTO PersonRoleAllDTO) {
        final QueryWrapper<?> queryWrapper = personRoleService.getPersonService().createQueryWrapper(PersonRoleAllDTO, "p.");
        queryWrapper.eq("pr.role_id", PersonRoleAllDTO.getRoleId());
        UserOperatorService.buildInSql(PersonRoleAllDTO, queryWrapper);
        final IPage<PersonRoleAllDTO> page = personRoleService.getBaseMapper().rolePersonPage(iPage, queryWrapper);
        queryWrapper.clear();
        return page;
    }
}